home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Graphics⁄Sound / PICTS to Movie / PICTStoMooV.c next >
Encoding:
C/C++ Source or Header  |  1992-09-14  |  16.0 KB  |  626 lines  |  [TEXT/KAHL]

  1. /*****************************************************************************\
  2. * Program: PICTStoMooV                                                        *
  3. *                                                                             *
  4. * Purpose:                                                                       *
  5. *    This is the source for a simple, modal utility to convert PICT files      *
  6. *    into QuickTime movies.  It was based upon the source in the QuickTime     *
  7. *   Developers (CD Version 1.0) sample-C-code directory: PICStoMovie.          *
  8. *                                                                              *
  9. *   It shows how to use the Standard Compression library to request           *
  10. *   compression setting from the user, how to use the Image Compression          *
  11. *    Manager to compress sequences of images, and how to use the Movie           *
  12. *   Toolbox to create simple QuickTime movies.                                  *
  13. * Usage:                                                                      *
  14. *    You will need to produce a sequence of PICT files of the format:          *
  15. *    "pictfilename.#" where "#" begins with 1 and continues until the movie    *
  16. *   ends, we reach the end of long integers = 2^32-1, or you run out of       *
  17. *   disk space.                                                                  *
  18. *   This program will ask you for the location of the first frame in the      *
  19. *   sequence (pictfilename.1) and will also give you the option of saving     *
  20. *   your movie in any of the currently installed codecs (compressions) that   *
  21. *   are available.                                                              *
  22. *                                                                             *
  23. * Created by: Reid Judd (ILLUMINATI@AppleLink.apple.com)                      *
  24. * Created on: September 12, 1992                                              *
  25. * Modified:                                                                   *
  26. \*****************************************************************************/
  27.  
  28.  
  29. #include <stdio.h>
  30.  
  31. #include <memory.h>
  32. #include <resources.h>
  33. #include <menus.h>
  34. #include <osevents.h>
  35. #include <events.h>
  36. #include <fonts.h>
  37. #include <errors.h>
  38.  
  39. #include "movies.h"
  40. #include "moviesformat.h"
  41.  
  42. /*    Think C likes all caps for routines in converted .o files. */
  43.  
  44. #ifdef    THINK_C
  45. #define    OpenStdCompression    OPENSTDCOMPRESSION
  46. #endif
  47.  
  48. #include "StdCompression.h"
  49.  
  50.  
  51. /*    Convenient macros for error checking. */
  52.  
  53. #define    BailOnError(result)    if (result) done();
  54. #define    BailOnNil(p)        if (!p) { result = -1; done();}
  55.  
  56.  
  57. /* Globals, needed for done() so it can deallocate resources.
  58.  */
  59.  
  60. short                dstMovieRefNum = 0;
  61. Movie                dstMovie = nil;
  62. ImageSequence        srcSeqID = 0;
  63. ImageSequence        dstSeqID = 0;
  64.     
  65. short                srcResRefNum = 0;
  66. Handle                compressedData = nil;
  67. ImageDescription    **idh = nil;
  68. WindowPtr            progressWindow = nil;
  69. GWorldPtr            pictGWorld = nil;
  70.  
  71. OSErr                result = noErr;
  72. short                quitFlag = FALSE;
  73.  
  74.  
  75. /* ---------------------------------------------------------------
  76.  * loadPict()
  77.  */
  78. PicHandle LoadPict( SFReply Rep )
  79. {
  80.     GDHandle saveDevice;
  81.     CGrafPtr savePort;
  82.      Str255 volName;
  83.     short curVolRefNum, refNum;
  84.     char buf[512];
  85.     long len;
  86.     PicHandle qdpic;
  87.     GWorldPtr mygw;
  88.  
  89.     result = FSOpen( Rep.fName, Rep.vRefNum, &refNum );
  90.     /* Error for file not found might mean that we're at the end of the sequence. */
  91.     if (result == fnfErr)
  92.       return NULL;  
  93.       
  94.     /* Error loading PICT file, can't open file (for reason other than fnfErr). */  
  95.     BailOnError(result);
  96.     
  97.     result = GetEOF( refNum, &len );
  98.     /* Error loading PICT file, can't get EOF */
  99.     BailOnError(result);
  100.     
  101.     if (len <= 512)
  102.     {
  103.         /* Pict file is empty */
  104.         result = FSClose( refNum );
  105.         return NULL;
  106.     }
  107.     result = SetFPos( refNum, fsFromStart, 512L );
  108.     /* Error loading PICT file, can't skip header */
  109.     BailOnError(result);    
  110.     
  111.     qdpic = (PicHandle) NewHandle( len - 512L );
  112.     /* Error loading PICT file, not enough memory */
  113.     BailOnNil( qdpic );
  114.  
  115.     HLock( qdpic );
  116.     len -= 512L;
  117.     result = FSRead( refNum, &len, (Ptr) (*qdpic) );
  118.     /* Error loading PICT file, can't read data */
  119.     BailOnError(result);        
  120.     
  121.     result = FSClose( refNum );
  122.     /* Error loading PICT file, can't close file */
  123.     BailOnError(result);
  124.     
  125.     /* unlock the handle to the pict. */    
  126.     HUnlock( qdpic );    
  127.  
  128.     return( qdpic );    
  129. }
  130.  
  131.  
  132. /* --------------------------------------------------------------- */
  133.  
  134. OSErr PICStoMovie()
  135. {
  136.     ComponentInstance    ci;
  137.     SCParams            p;
  138.     Point                where;
  139.     SFReply                inReply;
  140.     SFReply                outReply;
  141.     SFTypeList            typeList;
  142.     Rect                r;
  143.     short                hstate;
  144.     short                i;
  145.     
  146.     int                 framenum;
  147.     PicHandle            thePict;
  148.     Rect                pictRect;
  149.       long                compressedFrameSize;
  150.      
  151.      Media                dstMedia = nil;
  152.     Track                dstTrack = nil;
  153.     
  154.      GDHandle            progressGDevice;
  155.  
  156.     GDHandle             saveDevice;
  157.     CGrafPtr             savePort;    
  158.  
  159.     FSSpec                theFSSpec;
  160.     TimeScale            dstTimeScale;
  161.     long                frames_per_second = 12;
  162.     char                 filename[255];
  163.          
  164.     /*    Open the standard compression dialog component. */
  165.  
  166.     ci = OpenStdCompression();
  167.     BailOnNil(ci);
  168.  
  169.     /*
  170.      *    Fill in default settings for compression dialog.  These will be the
  171.      *    settings displayed when the compression dialog is first displayed.
  172.      *    Note that these settings are only set up once so that if the dialog
  173.      *    is used more than once, the user's settings from the previous time
  174.      *    will be the defaults for the next time.
  175.      */
  176.  
  177.     p.flags = scShowMotionSettings;
  178.     p.theCodecType = 'rpza';
  179.     p.theCodec = anyCodec;
  180.     p.spatialQuality = codecNormalQuality;
  181.     p.temporalQuality = codecNormalQuality;
  182.     p.depth = 8;
  183.     p.frameRate = (long)10 << 16;
  184.     p.keyFrameRate = 100;
  185.  
  186.  
  187.     /***************************************
  188.      *
  189.      *    Get source PICS file.
  190.      *
  191.      ***************************************/
  192.  
  193. ReaskFile:
  194.     
  195.     /*    Display SFGetFile dialog on best device and list only PICS files. */
  196.  
  197.     where.h = where.v = -2;
  198.     result = SCPositionDialog(ci,-4000,&where);
  199.     BailOnError(result);
  200.  
  201.     typeList[0] = 'PICT';
  202.  
  203.     SFGetFile(where,"\p",nil,1,typeList,nil,&inReply);
  204.     if (!inReply.good) {
  205.         quitFlag = true;
  206.         done();
  207.     }
  208.     
  209.     /* Load the first pict of the sequence.
  210.      *   Default movie name will be based upon this name.
  211.      */    
  212.     thePict = LoadPict( inReply );
  213.  
  214.     BailOnNil(thePict);
  215.     pictRect = (*thePict)->picFrame;
  216.  
  217.  
  218.     /***************************************
  219.      *
  220.      *    Ask user for compression parameters.
  221.      *
  222.      ***************************************/
  223.  
  224. ReaskOptions:
  225.     
  226.     /*
  227.      *    Save the purgeable state of the first PICT resource and then
  228.      *    make it unpurgeable.  We don't want the resource getting purged
  229.      *    while we're using it for the test image in standard compression.
  230.      */
  231.  
  232.     hstate = HGetState((Handle)thePict);
  233.     HNoPurge((Handle)thePict);
  234.     
  235.     /*    Set the PICT as the test image for standard compression. */
  236.     
  237.     SCSetTestImagePictHandle(ci,thePict,nil,0);
  238.     
  239.     /*
  240.      *    Get compression settings from user.  Center dialog on best screen
  241.      *    by setting where to (-2,-2)
  242.      */
  243.     
  244.     where.h = where.v = -2;
  245.     result = SCGetCompression(ci,&p,where);
  246.     
  247.     /*    Return picture to its previous purge state. */
  248.     
  249.     HSetState((Handle)thePict,hstate);
  250.     
  251.     /*    If the user selected Cancel, go ask for a new source PICS file. */
  252.     
  253.     if (result == 1)
  254.         goto ReaskFile;
  255.     BailOnError(result);
  256.  
  257.  
  258.     /***************************************
  259.      *
  260.      *    Get destination movie file.
  261.      *
  262.      ***************************************/
  263.  
  264.     /*
  265.      *    Default destination movie file name is source
  266.      *    movie file with ".movie" appended.  Save the 'filename',
  267.      *  we'll use it again to read in the rest of the frames.
  268.      */
  269.  
  270.     BlockMove(inReply.fName,outReply.fName,64);
  271.     outReply.fName[0] -= 2;    
  272.     p2cstr(outReply.fName);
  273.     strcpy(filename, (char *) outReply.fName );
  274.     sprintf( (char *) outReply.fName,"%s.movie", filename );
  275.     c2pstr(outReply.fName);
  276.         
  277.     /*    Position dialog on best device. */
  278.     
  279.     where.h = where.v = -2;
  280.     SCPositionDialog(ci,-3999,&where);
  281.     SFPutFile(where,"\pSave movie file as:",outReply.fName,nil,&outReply);
  282.     if (!outReply.good)
  283.         goto ReaskOptions;
  284.     
  285.     /*
  286.      *    inFile.fName and outFile.fName should be compared here to make sure
  287.      *    that they are not the same name.  If they are, an error will occur.
  288.      */
  289.  
  290.  
  291.     /***************************************
  292.      *
  293.      *    Open a progress window.
  294.      *
  295.      ***************************************/
  296.     
  297.     /*    Use standard compression routines to center window on best device. */
  298.     where.h = where.v = -2;
  299.  
  300.     r = pictRect;
  301.     SCPositionRect(ci,&r,&where);
  302.     progressWindow = NewCWindow(0,&r,outReply.fName,true,0,(WindowPtr)-1,false,0);
  303.     BailOnNil(progressWindow);
  304.     SetPort(progressWindow);
  305.     
  306.     /*    Set coordinate system of window to match that of picture. */
  307.     
  308.     SetOrigin(pictRect.left,pictRect.top);
  309.     progressGDevice = GetGDevice();
  310.  
  311.  
  312.     /***************************************
  313.      *
  314.      *    Prepare GWorld to draw pictures into.
  315.      *
  316.      ***************************************/
  317.  
  318.     result = NewGWorld(&pictGWorld,p.depth,&pictRect,nil,nil,0);
  319.     BailOnError(result);
  320.     LockPixels(pictGWorld->portPixMap);
  321.     
  322.     GetGWorld( &savePort, &saveDevice );
  323.  
  324.     SetGWorld(pictGWorld,nil);
  325.     EraseRect(&pictRect);
  326.     UnlockPixels(pictGWorld->portPixMap);
  327.  
  328.  
  329.     /***************************************
  330.      *
  331.      *    Set up movie file.
  332.      *
  333.      ***************************************/
  334.      
  335.     frames_per_second = (p.frameRate + 0x00008000) >> 16;
  336.     
  337.     /*    Create an FSSpec for the destination file. */
  338.         
  339.     result = FSMakeFSSpec(outReply.vRefNum,0,outReply.fName,&theFSSpec);
  340.     if (result && result != fnfErr)
  341.         done();
  342.         
  343.     /*    Create the movie file, deleting the old one if it exists. */
  344.         
  345.     result = CreateMovieFile(&theFSSpec,'TVOD',0,createMovieFileDeleteCurFile,
  346.                             &dstMovieRefNum,&dstMovie);
  347.     BailOnError(result);
  348.         
  349.     /*
  350.      *    Create a new track with the picture's dimensions.
  351.      *    Note that the dimensions are fixed point numbers.
  352.      */        
  353.     dstTrack = NewMovieTrack(dstMovie,(long)(pictRect.right - pictRect.left) << 16,
  354.                                     (long)(pictRect.bottom - pictRect.top) << 16,0);
  355.         
  356.     /*
  357.      *    Create a new video media with a time scale that is large
  358.      *    enough to accurately handle a fractional frame rate.
  359.      */        
  360.     dstTimeScale = 60;
  361.     while (frames_per_second > dstTimeScale)
  362.         dstTimeScale *= 10;
  363.     dstMedia = NewTrackMedia(dstTrack,VIDEO_TYPE,dstTimeScale,0,0);
  364.     result = BeginMediaEdits(dstMedia);
  365.     BailOnError(result);
  366.  
  367.  
  368.     /***************************************
  369.      *
  370.      *    Prepare compression sequence.
  371.      *
  372.      ***************************************/
  373.  
  374.     /*    Create an uninitialized image desciption. */
  375.  
  376.     idh = (ImageDescription**)NewHandle(sizeof(ImageDescription));
  377.     
  378.     /*
  379.      *    Find out how large the largest possible compressed frame
  380.      *    will be, allocated memory for it, and lock it down.
  381.      */
  382.     
  383.     result = GetMaxCompressionSize(pictGWorld->portPixMap,&pictRect,p.depth,p.spatialQuality,
  384.                 p.theCodecType,p.theCodec,&compressedFrameSize);
  385.     BailOnError(result);
  386.     compressedData = NewHandle(compressedFrameSize);
  387.     BailOnNil(compressedData);
  388.     HLock(compressedData);
  389.     
  390.     /*
  391.      *    Begin the compression sequence.  Note the the image desciption will still not be
  392.      *    initialized after the CompressSequenceBegin call, so now is not the time to be
  393.      *    looking at its contents.  This may change in the future.
  394.      */
  395.     
  396.     result = CompressSequenceBegin(&srcSeqID,pictGWorld->portPixMap,nil,&pictRect,nil,p.depth,
  397.                 p.theCodecType,p.theCodec,p.spatialQuality,p.temporalQuality,
  398.                 p.keyFrameRate,nil,codecFlagUpdatePrevious,idh);
  399.     BailOnError(result);
  400.  
  401.  
  402.     /***************************************
  403.      *
  404.      *    Create and compress frames. Start at frame 1 and continue 
  405.      *  until a there are no more frames with the format:
  406.      *        pictfile.#
  407.      *
  408.      ***************************************/
  409.  
  410.      framenum = 1;
  411.      while (quitFlag == FALSE) {
  412.  
  413.         unsigned char    similarity;
  414.         short            syncFlag;
  415.         TimeValue        duration;
  416.         long            flags;
  417.         
  418.         /*    Let the user abort by pressing the button. */
  419.         
  420.         if (Button())
  421.             break;
  422.  
  423.         /*    Draw the next image from the PICS file into the pictGWorld. */
  424.  
  425.         SetGWorld(pictGWorld,nil);
  426.         {            
  427.              Rect    r;
  428.              
  429.              r = (*thePict)->picFrame;
  430.             DrawPicture(thePict,&r);
  431.  
  432.             ReleaseResource((Handle)thePict); 
  433.            
  434.         }
  435.         SetGWorld((CGrafPtr)progressWindow,progressGDevice);
  436.  
  437.         /*
  438.          *    Compress a frame.  The flags are set to codecFlagUpdatePrevious + PreviousComp
  439.          *    so that frame differencing is done with the previous compressed image instead
  440.          *    of the previous source image.  This gives more accurate frame differencing.
  441.          *
  442.          *    Note that *compressedData is StripAddress-ed.  Data pointers passed into
  443.          *    image compression manager routines must be 32-bit clean.
  444.          */
  445.  
  446.         flags = codecFlagUpdatePrevious + codecFlagUpdatePreviousComp;
  447.         result = CompressSequenceFrame(srcSeqID,pictGWorld->portPixMap,&pictRect,flags,
  448.                     StripAddress(*compressedData),&compressedFrameSize,&similarity,nil);
  449.         BailOnError(result);
  450.  
  451.         /*
  452.          *    If this is the first frame, begin the decompression sequence for displaying
  453.          *    in the progress window.  We have to wait until after the the first
  454.          *    CompressSequenceFrame before we can use the image description handle idh because
  455.          *    it doesn't get initialized until after a CompressSequenceFrame.
  456.          */
  457.  
  458.         if (framenum == 1) {
  459.             result = DecompressSequenceBegin(&dstSeqID,idh,nil,nil,&pictRect,nil,ditherCopy,
  460.                         nil,0,codecNormalQuality,anyCodec);
  461.             BailOnError(result);
  462.         }
  463.  
  464.         /*
  465.          *    Add the newly compressed frame to the dstMedia.  We set the syncFlag based on
  466.          *    the similarity returned by CompressSequenceFrame.  If the similarity is non-zero,
  467.          *    the frame has some frame differencing in it and therefore is not a sync sample.
  468.          *    If the similarity is zero, there is no frame differencing and the frame is a
  469.          *    sync/key frame.
  470.          */
  471.          
  472.         /* default duration is set to 12 frames/sec: 60/12 = 5 */
  473.         duration = dstTimeScale/frames_per_second;  
  474.  
  475.         syncFlag = (similarity ? mediaSampleNotSync : 0);
  476.         result = AddMediaSample(dstMedia,compressedData,0,compressedFrameSize,
  477.                     duration,
  478.                     (SampleDescriptionHandle)idh,1,syncFlag,nil);
  479.         BailOnError(result);
  480.         
  481.         /*
  482.          *    Decompress the current frame to the progress window.  Again notice that
  483.          *    the dereferenced compressedData handle was StripAddressed.
  484.          */
  485.         
  486.         if (progressWindow) {
  487.             CodecFlags    outFlags;
  488.             result = DecompressSequenceFrame(dstSeqID,StripAddress(*compressedData),0,&outFlags,nil);
  489.             BailOnError(result);
  490.         }
  491.         
  492.         
  493.         /* Get the next frame in the sequence.   
  494.          */
  495.         framenum++;
  496.     
  497.         p2cstr( inReply.fName );
  498.          sprintf( (char *) inReply.fName, "%s.%d", filename, framenum );
  499.         c2pstr( inReply.fName );
  500.  
  501.         thePict = LoadPict( inReply );
  502.          if (thePict == NULL)
  503.           quitFlag = TRUE;                 
  504.     }
  505.     HUnlock((Handle)compressedData);
  506.  
  507.  
  508.     /***************************************
  509.      *
  510.      *    Finish creating destination movie file.
  511.      *
  512.      ***************************************/
  513.  
  514.     {
  515.         short resID = 0;
  516.         result = EndMediaEdits(dstMedia);
  517.         BailOnError(result);
  518.         InsertMediaIntoTrack(dstTrack,0,0,GetMediaDuration(dstMedia),1L<<16);
  519.         BailOnError(result = GetMoviesError());
  520.      
  521.         result = AddMovieResource(dstMovie,dstMovieRefNum,
  522.                             &resID, outReply.fName );
  523.  
  524.         BailOnError(result);
  525.  
  526.         dstMedia = nil;
  527.     }
  528.     quitFlag = false;
  529.  
  530.  
  531.     /***************************************
  532.      *
  533.      *    Deallocate everything that was created.
  534.      *
  535.      ***************************************/
  536.      
  537.      done();
  538.      
  539.     /*
  540.      *    If an error did not occur and the user did not
  541.      *    ask to quit, go repeat the conversion process
  542.      *    for a new file.
  543.      */
  544.     
  545.     if (result == noErr && !quitFlag)
  546.         goto ReaskFile;
  547.     
  548.     if (ci)
  549.         CloseComponent(ci);    
  550. }
  551. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
  552.  * done:
  553.  *        Deallocate everything that was created.     If no error, 
  554.  *        continue, otherwise we're bailing out.
  555.  */
  556. done()
  557. {
  558.  
  559.     if (dstMovieRefNum) {
  560.         CloseMovieFile(dstMovieRefNum);
  561.         dstMovieRefNum = 0;
  562.     }
  563.  
  564.     if (dstMovie) {
  565.         DisposeMovie(dstMovie);
  566.         dstMovie = nil;
  567.     }
  568.  
  569.     if (srcSeqID) {
  570.         CDSequenceEnd(srcSeqID);
  571.         srcSeqID = 0;
  572.     }
  573.     
  574.     if (dstSeqID) {
  575.         CDSequenceEnd(dstSeqID);
  576.         dstSeqID = 0;
  577.     }    
  578.  
  579.     if (srcResRefNum) {
  580.         CloseResFile(srcResRefNum);
  581.         srcResRefNum = 0;
  582.     }
  583.     
  584.     if (compressedData) {
  585.         DisposHandle(compressedData);
  586.         compressedData = nil;
  587.     }
  588.     
  589.     if (idh) {
  590.         DisposHandle((Handle)idh);
  591.         idh = nil;
  592.     }
  593.     
  594.     if (progressWindow) {
  595.         CloseWindow(progressWindow);
  596.         progressWindow = nil;
  597.     }
  598.     
  599.     if (pictGWorld) {
  600.         DisposeGWorld(pictGWorld);
  601.         pictGWorld = nil;
  602.     }
  603.     
  604.     if ( (result != noErr) || (quitFlag == TRUE) )
  605.       exit();
  606. }     
  607. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  608.  
  609.  
  610. void main()
  611. {
  612.     InitGraf(&qd.thePort);
  613.     InitFonts();
  614.     FlushEvents(0xffff,0);
  615.     InitWindows();
  616.     InitMenus();
  617.     InitDialogs(0);
  618.     TEInit();
  619.     InitCursor();
  620.     MaxApplZone();
  621.     
  622.     EnterMovies();
  623.     PICStoMovie();
  624. }
  625.  
  626.